/* e100_cmd.c  -  e100_enable_irq, e100_disable_irq, 
 * 				e100_exec_cmd, e100_exec_cb */

#include <xinu.h>

/*------------------------------------------------------------------------
 * e100_disable_irq - Mask off interrupt generation on the ethptr
 *------------------------------------------------------------------------
 */
void e100_disable_irq(
	struct ether *ethptr
	)
{
	outb(ethptr->iobase + E100_CMD_HI, irq_mask_all);
	e100_write_flush(ethptr->iobase);
}

/*------------------------------------------------------------------------
 * e100_enable_irq - Enable default interrupt generation settings
 *------------------------------------------------------------------------
 */
void e100_enable_irq(
	struct ether *ethptr
	)
{
	outb(ethptr->iobase + E100_CMD_HI, irq_mask_none);
	e100_write_flush(ethptr->iobase);
}

/*------------------------------------------------------------------------
 * Execute command using SCB
 *------------------------------------------------------------------------
 */
#define E100_WAIT_SCB_TIMEOUT 20000 /* we might have to wait 100ms!!! */
#define E100_WAIT_SCB_FAST 20       /* delay like the old code */
int e100_exec_cmd(
	struct ether *ethptr, uint8 cmd, uint32 dma_addr
	)
{
	unsigned int i;
	/* Previous command is accepted when SCB clears */
	for (i = 0; i < E100_WAIT_SCB_TIMEOUT; i++) {
		if (!inb( ethptr->iobase + E100_CMD_LO))
			break;
		//cpu_relax();
		if (i > E100_WAIT_SCB_FAST)
			DELAY(5);
	}
	if (i == E100_WAIT_SCB_TIMEOUT) {
		kprintf("SCB command waited too long!\r\n");
		return SYSERR;
	}
	if (cmd != cuc_resume)
		outl( ethptr->iobase + E100_GEN_PTR, dma_addr );
	outb( ethptr->iobase + E100_CMD_LO, cmd);
	return OK;
}

/*------------------------------------------------------------------------
 * Execute command using Command Block
 *------------------------------------------------------------------------
 */
int e100_exec_cb(
	struct ether *ethptr,
	void (*cb_prepare)(struct ether *, struct e100_tx_desc *)
	)
{
	struct e100_tx_desc *cb;
	struct e100_tx_desc *prev;
	int32 retval;

	//cb = ethptr->txTail;
	cb = (struct e100_tx_desc *)ethptr->txRing + ethptr->txTail;
	prev = (struct e100_tx_desc *)ethptr->txRing + (ethptr->txTail-1)%ethptr->txRingSize;
	//ethptr->txTail = cb->next;
	ethptr->txTail 
                = (ethptr->txTail + 1) % ethptr->txRingSize;

	cb_prepare(ethptr, cb);

	if (cb->status & cb_complete) {
		kprintf("cb->status == cb_complete\r\n");
	}
	if (cb->status & cb_ok) {
		kprintf("cb->status == cb_ok\r\n");
	}
	if (prev->status & cb_complete) {
		kprintf("prev->status == cb_complete\r\n");
	}
	if (prev->status & cb_ok) {
		kprintf("prev->status == cb_ok\r\n");
	}

	/* Order is important otherwise we'll be in a race with h/w:
	 * set S-bit in current first, then clear S-bit in previous. */
	cb->command |= cb_s;
	
	//cb->prev->command &= ~cb_s;
	((struct e100_tx_desc *)ethptr->txRing + (ethptr->txTail - 2) % \
	ethptr->txRingSize)->command &= ~cb_s;

	while (ethptr->txHead != ethptr->txTail) {
		retval = e100_exec_cmd(ethptr, ethptr->cuc_cmd,
			(uint32)ethptr->txRing);
		kprintf("e100_exec_cmd(cuc_cmd) = %d\r\n", retval);
		kprintf("cuc_cmd = %x\r\n", ethptr->cuc_cmd);
		if (retval) {
			break;
		} else {
			ethptr->cuc_cmd = cuc_resume;
			//ethptr->txHead = ethptr->txHead->next;
			ethptr->txHead 
                = (ethptr->txHead + 1) % ethptr->txRingSize;
		}
	}
	return OK;
}
